package com.autonavi.yunda.yunji.core.service;

import com.autonavi.yunda.yunji.common.exception.AmapPreconditions;
import com.autonavi.yunda.yunji.common.utils.JsonUtils;
import com.autonavi.yunda.yunji.core.enums.*;
import com.autonavi.yunda.yunji.core.model.dto.BpmsApprovalDTO;
import com.autonavi.yunda.yunji.core.model.dto.InterfaceConfigDTO;
import com.autonavi.yunda.yunji.core.model.dto.PartUnitDTO;
import com.autonavi.yunda.yunji.core.model.mappers.BpmsApprovalMapper;
import com.autonavi.yunda.yunji.core.model.mappers.PartUnitMapper;
import com.autonavi.yunda.yunji.core.model.vo.ComponentData;
import com.autonavi.yunda.yunji.core.model.vo.PartUnitExtraData;
import com.autonavi.yunda.yunji.core.service.thirdpartyservice.FalconInterfaceService;
import com.autonavi.yunda.yunji.core.service.thirdpartyservice.YuncangService;
import com.autonavi.yunda.yunji.core.service.thirdpartyservice.vo.BasePartUnitVO;
import com.autonavi.yunda.yunji.core.service.thirdpartyservice.vo.PartVO;
import com.autonavi.yunda.yunji.core.service.thirdpartyservice.vo.UnitVO;
import com.autonavi.yunda.yunji.core.vo.response.FalconVO;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static com.autonavi.yunda.yunji.common.exception.AmapExceptionCode.DATA_NOT_EXIST;

@Component
@Slf4j
public class PartUnitService {
    @Resource
    private PartUnitMapper partUnitMapper;
    @Resource
    private YuncangService yuncangService;
    @Resource
    private FalconInterfaceService falconInterfaceService;
    @Resource
    private BpmsApprovalMapper bpmsApprovalMapper;

    @Transactional(rollbackFor = Exception.class)
    public Long add(PartUnitDTO partUnitDTO) {
        partUnitMapper.insert(partUnitDTO);
        return partUnitDTO.getId();
    }

    public void update(UpdateWrapper<PartUnitDTO> updateWrapper) {
        partUnitMapper.update(null, updateWrapper);
    }

    public PartUnitDTO findByIdOrThrow(Long id) {
        AmapPreconditions.checkNotNull(id);
        PartUnitDTO dto = partUnitMapper.selectById(id);
        AmapPreconditions.checkArgumentForUser(dto != null, "不存在该接口 id = " + id);
        return dto;
    }

    @Transactional(rollbackFor = Exception.class)
    public void syncToYuncang(Long id, String operator) {
        PartUnitDTO dto = findByIdOrThrow(id);
        AmapPreconditions.checkArgument(Objects.nonNull(dto), DATA_NOT_EXIST, "partUnitDTO不存在");
        FalconVO falconVO = falconInterfaceService.getFalconById(dto.getFalconId(), operator);
        AmapPreconditions.checkArgument(Objects.nonNull(falconVO), DATA_NOT_EXIST, "falconVO不存在");
        AmapPreconditions.checkArgumentForUser(Objects.nonNull(falconVO.getOptions()), "falcon返回结果异常");

        BasePartUnitVO partUnitVO = ComponentType.part.equals(dto.getType()) ?
                PartVO.from(dto, falconVO.getOptions(), operator) :
                UnitVO.from(dto, falconVO.getOptions(), operator);

        Long yuncangId = yuncangService.syncToYuncang(dto.getType(), partUnitVO);

        UpdateWrapper<PartUnitDTO> wrapper = new UpdateWrapper<PartUnitDTO>()
                .eq("id", id).set("yuncang_id", yuncangId);
        update(wrapper);
        updateStatus(id, PartUnitInterfaceStatus.SYNCED);

    }

    public void publish(Long id, String operator) {
        PartUnitDTO dto = findByIdOrThrow(id);
        AmapPreconditions.checkArgumentForUser(
                dto.getStatus() == PartUnitInterfaceStatus.SYNCED,
                "已同步的接口才能进行发布");
        BasePartUnitVO partUnitVO = new BasePartUnitVO();
        partUnitVO.setId(Long.valueOf(dto.getYuncangId()));
        partUnitVO.setUpdateMember(operator);
        partUnitVO.setStatus(1);
        partUnitVO.setWorkBdId(dto.getBdId());
        partUnitVO.setWorkNo(operator);
        yuncangService.syncToYuncang(dto.getType(), partUnitVO);
        updateStatus(id, PartUnitInterfaceStatus.PUBLISHED);
    }

    public void deleteById(Long id) {
        PartUnitDTO dto = findByIdOrThrow(id);
        AmapPreconditions.checkArgumentForUser(dto.getStatus() != PartUnitInterfaceStatus.SYNCED, "已同步的接口不能删除");
        partUnitMapper.deleteById(id);
    }

    public void updateStatus(Long id, PartUnitInterfaceStatus status) {
        UpdateWrapper<PartUnitDTO> wrapper = new UpdateWrapper<PartUnitDTO>()
                .eq("id", id).set("status", status);
        update(wrapper);
    }

    public void updateExtraData(Long id, PartUnitExtraData extraData) {
        UpdateWrapper<PartUnitDTO> wrapper = new UpdateWrapper<PartUnitDTO>()
                .eq("id", id).set("extra_data", JsonUtils.toString(extraData));
        update(wrapper);
    }

    public Long addByInterface(InterfaceConfigDTO dto, String userId, Long bdId) {
        List<ComponentData> partList = yuncangService.fetchComponentData(dto.getInterfaceData().partIdList, ComponentType.part, bdId, userId);
        List<ComponentData> unitList = yuncangService.fetchComponentData(dto.getInterfaceData().unitIdList, ComponentType.unit, bdId, userId);
        AmapPreconditions.checkArgumentForUser(dto.getInterfaceData().falconId != null, "无falconId，请重新发布接口再次尝试");
        PartUnitDTO partUnitDTO = PartUnitDTO.builder()
                .code(DigestUtils.md5DigestAsHex(dto.getUri().getBytes()))
                .version(dto.getVersion())
                .type(ComponentType.unit)
                .createUser(userId)
                .updateUser(userId)
                .sourceId(dto.getId())
                .sourceType(ComponentSourceType.BUILD)
                .tags(new ArrayList<>())
                .partList(partList)
                .unitList(unitList)
                .falconId(dto.getInterfaceData().falconId)
                .title(dto.getName())
                .status(PartUnitInterfaceStatus.INIT)
                .bdId(bdId)
                .build();
        return add(partUnitDTO);
    }

    public void updateByInterface(InterfaceConfigDTO dto, String userId, Long bdId) {
        AmapPreconditions.checkNotNull(dto.getInterfaceData().partUnitId);
        List<ComponentData> partList = yuncangService.fetchComponentData(dto.getInterfaceData().partIdList, ComponentType.part, bdId, userId);
        List<ComponentData> unitList = yuncangService.fetchComponentData(dto.getInterfaceData().unitIdList, ComponentType.unit, bdId, userId);
        UpdateWrapper<PartUnitDTO> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq("id", dto.getInterfaceData().partUnitId);
        updateWrapper.set("part_list", JsonUtils.toString(partList));
        updateWrapper.set("unit_list", JsonUtils.toString(unitList));
        updateWrapper.set("falcon_id", dto.getInterfaceData().getFalconId());
        updateWrapper.set("title", dto.getName());
        updateWrapper.set("update_user", userId);
        update(updateWrapper);
    }

    @Transactional
    public void updateTestStatus(Map<String, String> params) {
        Long interfaceId = Long.valueOf(params.get("interfaceId"));
        AmapPreconditions.checkNotNull(interfaceId);
        String approvalStatus = params.get("finishStatus");
        if (StringUtils.isNotBlank(approvalStatus) && "APPROVAL".equals(approvalStatus)) {
            try {
                PartUnitTestStatus status = PartUnitTestStatus.valueOf(params.get("testStatus"));
                String testId = params.get("testId");
                AmapPreconditions.checkNotNull(interfaceId);
                PartUnitDTO partUnitDTO = findByIdOrThrow(interfaceId);
                PartUnitExtraData extraData = partUnitDTO.getExtraData();
                if (!status.equals(PartUnitTestStatus.TEST_IGNORE)) {
                    AmapPreconditions.checkArgumentForUser(StringUtils.isNotBlank(testId), "testId is blank");
                    testId = null;
                }
                extraData.setTestId(testId);
                extraData.setTestStatus(status);
                updateExtraData(interfaceId, extraData);
            } catch (Exception e) {
                log.error("updateTestStatus error", e);
                initComponentStatus(interfaceId);
            }
        } else {
            //撤回或拒绝
            initComponentStatus(interfaceId);
        }
    }

    private void initComponentStatus(Long interfaceId) {
        //撤回或者异常将状态重置
        PartUnitDTO partUnitDTO = findByIdOrThrow(interfaceId);
        PartUnitExtraData extraData = partUnitDTO.getExtraData();
        if (!extraData.getTestStatus().equals(PartUnitTestStatus.TEST_SUCCESS)) {
            extraData.setTestId(null);
            extraData.setTestBPMSFlowId(null);
            extraData.setTestStatus(PartUnitTestStatus.UNTESTED);
            updateExtraData(interfaceId, extraData);
        }
    }

    @Transactional
    public void submitShelf(Long id, String procInstId) {
        AmapPreconditions.checkNotNull(procInstId);
        PartUnitDTO dto = findByIdOrThrow(id);
        BpmsApprovalDTO approvalDTO = BpmsApprovalDTO.builder()
                .type(BpmsCallBackType.ycOnShelfApproval)
                .procInstId(procInstId)
                .relationId(id)
                .status(BpmsApprovalStatus.INIT)
                .build();
        bpmsApprovalMapper.insert(approvalDTO);
        PartUnitExtraData extraData = dto.getExtraData();
        extraData.setShelfProcInstId(procInstId);
        updateExtraData(id, extraData);
        updateStatus(id, PartUnitInterfaceStatus.SHELF_REVIEW);
    }

    public void updateShelfStatus(Long componentId, BpmsApprovalStatus approvalStatus) {
        PartUnitDTO partUnitDTO = findByIdOrThrow(componentId);
        if (BpmsApprovalStatus.AGREE.equals(approvalStatus)) {
            updateStatus(partUnitDTO.getId(), PartUnitInterfaceStatus.SHELF);
        } else {
            updateStatus(partUnitDTO.getId(), PartUnitInterfaceStatus.PUBLISHED);
        }
    }
}
